package ga;

import ga.GA.ParametroFormalR1;
import ga.GA.ParametroFormalR2;
import ga.GA.ParametroFormalR2Debug;
import ga.GA.ParametroRealesR1;
import ga.GA.ParametroRealesR1Debug;
import ga.GA.ParametroRealesR2;
import ga.GA.ParametroRealesR2Debug;

import java.io.IOException;


public class ConstructorArboles {	
	
	public static final boolean DEBUG=false;
	private Token token;
	private GA tGraAtrib = new GA();
	private AnalizadorLexicoLengDescAlgoritmica anaLexico;
	
	public ConstructorArboles(AnalizadorLexicoLengDescAlgoritmica anaLexico) {
	     this.anaLexico = anaLexico;
	}
	  

	public GA.Programa parse() throws IOException {
		 token = anaLexico.sigToken();
	     GA.Programa aDeProg = recProg();
	     rec(CatLexica.EOF);
	     return aDeProg;
	}
	
	private GA.Programa recProg() throws IOException{
		GA.Bloque bloque = recBloque();
		return progR1(bloque);
	}

	private GA.Bloque recBloque() throws IOException{
		/*
		 * Bloque ::= IDEN RBloque
			    Bloque.a = RBloque.a
			    RBloque.ah = Iden.lex
			Bloque ::= Dec2 RDecs &  Insts
			    Bloque.a = bloqueR1(RDecs.a,Insts.a)
			    RDecs.ah = decsR2(Dec2.a)
			Bloque::= Inst2 RInsts
			    Bloque.a = bloqueR2(RInsts.a)
			
			RInsts.ah = instsR2(Inst2.a)
		 */
		if(tokenActual(CatLexica.IDENTIFICADOR))
		{
			Token tDeId = token;			
			rec(CatLexica.IDENTIFICADOR);
			GA.Bloque rBloque = recRBloque(tDeId);
			return rBloque;
		}
		else if (tokenActual(CatLexica.TIPO) || tokenActual(CatLexica.PROCEDIMIENTO) || 
				tokenActual(CatLexica.INT) || tokenActual(CatLexica.BOOLEAN) ||
				tokenActual(CatLexica.TABLA) || tokenActual(CatLexica.REGISTRO) ||
				tokenActual(CatLexica.PUNTERO) || tokenActual(CatLexica.IDENTIFICADOR)){
			GA.Dec dec2 = recDec2();
			GA.Decs declaraciones = recRDecs(decsR2(dec2));
			rec(CatLexica.AMPERSAND);
			GA.Insts instrucciones = recInsts();
			return bloqueR1(declaraciones,instrucciones);
		}
		else{
			GA.Inst inst2 = recInst2();
			GA.Insts rInsts = recRInsts(instsR2(inst2));
			
			return bloqueR2(rInsts);
		}
	}
	
	private GA.Bloque recRBloque(Token iden_a) throws IOException{
		/*
		 * RBloque ::= IDEN RDecs & Insts
			    RBloque.a = bloqueR1(RDecs.a,Insts.a)
				RDecs.ah = decsR2(decR1(tipoR6(RBloque.ah, IDEN.lex))
			RBloque ::= RInst RInsts
			    RBloque.a = bloqueR2(RInsts.a)
			    RInsts.ah = instsR2(RInst.a)
			    RInst.ah = Rbloque.ah
		 */
		if(tokenActual(CatLexica.IDENTIFICADOR))
		{
			Token tDeId = token;			
			rec(CatLexica.IDENTIFICADOR);
			GA.Decs rDecs = recRDecs(decsR2(decR1(tipoR6(iden_a), tDeId)));
			rec(CatLexica.AMPERSAND);
			GA.Insts instrucciones = recInsts();
			return bloqueR1(rDecs,instrucciones);
		}
		else {
			GA.Inst rInst = recRInst(iden_a);
			GA.Insts instrucciones = recRInsts(instsR2(rInst));
			return bloqueR2(instrucciones);
		}
	}
	
//DUDA: NO LLAMAN NUNCA A ESTE METODO!!... no aparece en otro lado del codigo
	private GA.Decs recDecs() throws IOException{
		/*
		 * Decs ::= IDEN IDEN RDecs
			    RDecs.ah = decsR2(decR1(tipoR6(IDEN(0).lex), IDEN(1).lex))
			    Decs.a = RDecs.a
			Decs ::= Dec2 RDecs
			    RDecs.ah = decsR2(Dec2.a)
			    Decs.a = RDecs.a
		 */
		if(tokenActual(CatLexica.IDENTIFICADOR))
		{
			Token tDeId = token;			
			rec(CatLexica.IDENTIFICADOR);
			Token tDeId2 = token;			
			rec(CatLexica.IDENTIFICADOR);
			
			GA.Decs rDecs = decsR2(decR1(tipoR6(tDeId), tDeId2));
			return rDecs;
		}
		else
		{
			GA.Dec dec_a = recDec2();
			GA.Decs rDecs = recRDecs(decsR2(dec_a));
			
			return  rDecs;
		}
	}
	
	private GA.Decs recRDecs(GA.Decs declaracion_a) throws IOException{
	/*	
		if (tokenActual(CatLexica.PYC)){
			rec(CatLexica.PYC);
			GA.Dec declaracion = recDec();
			GA.Decs rDeclaraciones = recRDecs(decsR1(declaracion_a, declaracion));
			
			return rDeclaraciones;
		}
		else
			return declaracion_a;
	*/ 
	
		GA.Decs rDeclaraciones;
		GA.Dec declaracion;
		while(tokenActual(CatLexica.PUNTOCOMA)){
			rec(CatLexica.PUNTOCOMA);
			declaracion = recDec();
			rDeclaraciones = decsR1(declaracion_a, declaracion);
			declaracion_a = rDeclaraciones;
		}
		return declaracion_a;
	}
	
	private GA.Dec recDec() throws IOException{
		
		if(tokenActual(CatLexica.IDENTIFICADOR)) {
			Token tDeId = token;
			rec(CatLexica.IDENTIFICADOR);
			Token tDeId2 = token;
			rec(CatLexica.IDENTIFICADOR);
			
			return decR1(tipoR6(tDeId), tDeId2);
		}
		else {
			return recDec2();
		}
	}
	
	private GA.Dec recDec2() throws IOException{
		
		if(tokenActual(CatLexica.PROCEDIMIENTO)) {
			
			rec(CatLexica.PROCEDIMIENTO);
			
			Token tDeId = token;
			rec(CatLexica.IDENTIFICADOR);
			
			rec(CatLexica.PARENTESISAPERTURA);
			GA.ParametrosFormales paramForm = recParamsFormales();
			rec(CatLexica.PARENTESISCIERRE);
			rec(CatLexica.LLAVEAPERTURA);
			GA.Bloque bloque = recBloque();
			rec(CatLexica.LLAVECIERRE);
			
			return decR3(tDeId, paramForm, bloque);
		}
		else if(tokenActual(CatLexica.TIPO)) {
			
			rec(CatLexica.TIPO);
			GA.Tipo tipo = recTipoR1();
			
			Token tDeId = token;			
			rec(CatLexica.IDENTIFICADOR);
			
			return decR2(tipo, tDeId);
		}
		else {
			
			GA.Tipo tipo = recTipo2();
			Token tDeId = token;
			rec(CatLexica.IDENTIFICADOR);
		
			return decR1(tipo,tDeId);
		}
	}
	
	private GA.ParametrosFormales recParamsFormales() throws IOException{
		if(tokenActual(CatLexica.INT) || tokenActual(CatLexica.BOOLEAN) ||
				tokenActual(CatLexica.TABLA) || tokenActual(CatLexica.REGISTRO) ||
				tokenActual(CatLexica.PUNTERO) || tokenActual(CatLexica.IDENTIFICADOR) || tokenActual(CatLexica.VARIABLE)){
			
			GA.ListaParametrosFormales lParamForm = recListaParamsFormales();
			
			return paramFormR1(lParamForm);
		}
		else{
			return paramFormR2();
		}
			
	}
	
	private GA.ListaParametrosFormales recListaParamsFormales() throws IOException{
		
		GA.ParametroFormal paramFormal_a = recParametroFormal();
		GA.ListaParametrosFormales rListParamFormal = recRListaParamsFormales(listaParamsFormalesR2(paramFormal_a));
		
		return rListParamFormal;
	}
	
	private GA.ListaParametrosFormales recRListaParamsFormales(GA.ListaParametrosFormales lParamForm_a) throws IOException{
		if(tokenActual(CatLexica.COMA)){
			rec(CatLexica.COMA);
			GA.ParametroFormal paramFormal = recParametroFormal();
			GA.ListaParametrosFormales rListParamFormal = recRListaParamsFormales(listaParamsFormalesR1(lParamForm_a,paramFormal));
			
			return rListParamFormal;
		}
		else{
			return lParamForm_a;
		}
	}
	
	private GA.ParametroFormal recParametroFormal() throws IOException{
		if(tokenActual(CatLexica.VARIABLE)){
			rec(CatLexica.VARIABLE);
			GA.Tipo tipo = recTipoR1();
			
			Token tDeId = token;
			rec(CatLexica.IDENTIFICADOR);
			
			return paramFormalR2(tipo,tDeId);
		}
		else{
			GA.Tipo tipo = recTipoR1();
			
			Token tDeId = token;
			rec(CatLexica.IDENTIFICADOR);
			
			return paramFormalR1(tipo,tDeId);
		}
	}
	
	private GA.Tipo recTipoR1() throws IOException{
		/*
		 * Tipo ::= IDEN
			    Tipo.a = tipoR6(IDEN.lex)
			Tipo ::= Tipo2
			    Tipo.a = Tipo2.a
		 */
		if(tokenActual(CatLexica.IDENTIFICADOR)){
			Token tDeId = token;
			rec(CatLexica.IDENTIFICADOR);
			
			return tipoR6(tDeId);
		}
		else{
			return recTipo2();
		}
	}

	private GA.Tipo recTipo2() throws IOException{
		
		if (tokenActual(CatLexica.INT)){
			rec(CatLexica.INT);
			return tipoR1();
		}
		else if (tokenActual(CatLexica.BOOLEAN)){
			rec(CatLexica.BOOLEAN);
			return tipoR2();
		}
		
		else if(tokenActual(CatLexica.REGISTRO)){
			rec(CatLexica.REGISTRO);
			rec(CatLexica.LLAVEAPERTURA);
			GA.Campos campos = recCampos();
			rec(CatLexica.LLAVECIERRE);
			return tipoR4(campos);
		}
		else if (tokenActual(CatLexica.TABLA)){
			rec(CatLexica.TABLA);
			rec(CatLexica.CORCHETEAPERTURA);
			Token tDeNum = token;
			rec(CatLexica.NUMERO);
			rec(CatLexica.CORCHETECIERRE);
			rec(CatLexica.DE);
			GA.Tipo tipo = recTipoR1();
			return tipoR3(tDeNum, tipo);
		}
		else if(tokenActual(CatLexica.IDENTIFICADOR)){
			Token tDeId = token;
			rec(CatLexica.IDENTIFICADOR);
			return tipoR6(tDeId);
		}
		else if(tokenActual(CatLexica.PUNTERO)){
			rec(CatLexica.PUNTERO);
			GA.Tipo tipo = recTipoR1();
			return tipoR5(tipo);
		}
		else{
			errorSintactico(CatLexica.BOOLEAN, CatLexica.INT, CatLexica.TABLA, 
			CatLexica.REGISTRO,	CatLexica.PUNTERO, CatLexica.IDENTIFICADOR);
			return null;
		}
	}
	
	private GA.Campos recCampos() throws IOException{
		
		GA.Campo campo_a = recCampo();
		GA.Campos rCampos = recRCampos(camposR2(campo_a));
		
		return rCampos;
	}
	
	private GA.Campos recRCampos(GA.Campos campo_a) throws IOException{
		
	/*	if(tokenActual(CatLexica.PYC)){
			rec(CatLexica.PYC);
			GA.Campo campo = recCampo();
			GA.Campos rCampos = recRCampos(camposR1(campo_a, campo));
			
			return rCampos;
		}
		else{
			return campo_a;
		}
		*/
		GA.Campo campo;
		GA.Campos rCampos;
		while(tokenActual(CatLexica.PUNTOCOMA))
		{
			rec(CatLexica.PUNTOCOMA);
			campo = recCampo();
			rCampos = camposR1(campo_a, campo);
			campo_a = rCampos;
		}
		
		return campo_a;
	}
	
	private GA.Campo recCampo() throws IOException{
		
		GA.Tipo tipo = recTipoR1();
		
		Token tDeId = token;
		rec(CatLexica.IDENTIFICADOR);
		
		return campoR1(tipo,tDeId);
	}
	
	private GA.Insts recInsts() throws IOException{
		
		if(tokenActual(CatLexica.IDENTIFICADOR)){
			Token tDeId = token;
			rec(CatLexica.IDENTIFICADOR);
			GA.Inst rInst = recRInst(tDeId);
			GA.Insts rInsts = recRInsts(instsR2(rInst));
			return rInsts;
		}
		else{
			GA.Inst inst = recInst2();
			GA.Insts rInsts = recRInsts(instsR2(inst));
			
			return rInsts;
		}
	}
	
	private GA.Insts recRInsts(GA.Insts instruccion_a) throws IOException{
		/*
		if(tokenActual(CatLexica.PYC)){
			rec(CatLexica.PYC);
			GA.Inst instruccion = recInst();
			GA.Insts rInstrucciones = recRInsts(instsR1(instruccion_a, instruccion));
			
			return rInstrucciones;
		}
		else{
			return instruccion_a;
		}
		*/
		GA.Inst instruccion;
		GA.Insts instrucciones;
		while(tokenActual(CatLexica.PUNTOCOMA))
		{
			rec(CatLexica.PUNTOCOMA);
			instruccion = recInst();
			instrucciones = instsR1(instruccion_a, instruccion);
			instruccion_a = instrucciones;
		}
		
		return instruccion_a;
	}
	
	private GA.Inst recInst() throws IOException{
		
		if(tokenActual(CatLexica.IDENTIFICADOR)){
			Token tDeIden = token;
			rec(CatLexica.IDENTIFICADOR);
			
			GA.Inst rInst = recRInst(tDeIden);
			
			return rInst;
			
		}
		else {
	          return recInst2();
	    }
	}

	private GA.Inst recInst2() throws IOException{
		
		if(tokenActual(CatLexica.IF)){
			GA.IIF iif = recIIF();
			
			return instR2(iif);
			
		} else if(tokenActual(CatLexica.DO)){
			GA.IDO ido = recIDO();
			
			return instR3(ido);
			
		} else if(tokenActual(CatLexica.NEW)){
			GA.INew iNew = recINew();
			
			return instR4(iNew);
			
		} else if(tokenActual(CatLexica.DELETE)){
			GA.IDispose iDispose = recIDispose();
			
			return instR5(iDispose);
			
		} else if(tokenActual(CatLexica.LECTURA)){
			GA.ILectura iLectura = recILectura();
			
			return instR6(iLectura);
			
		} else if(tokenActual(CatLexica.ESCRITURA)){
			GA.IEscritura iEscritura = recIEscritura();
			
			return instR7(iEscritura);
			
		}
		else {
	          errorSintactico(CatLexica.IDENTIFICADOR, CatLexica.IF, CatLexica.DO, CatLexica.NEW, CatLexica.DELETE, CatLexica.LECTURA, CatLexica.ESCRITURA);
	          return null;
	    }
	}
	
	private GA.Inst recRInst(Token iden_a) throws IOException{
		if(tokenActual(CatLexica.PARENTESISAPERTURA)){
			rec(CatLexica.PARENTESISAPERTURA);
			GA.ParametrosReales paramReales = recParamsReales();
			rec(CatLexica.PARENTESISCIERRE);
			
			return instR8(iLlamadaR1(iden_a,paramReales));
		} else{
			GA.Mem rmem = recRMem(memR1(iden_a));
			rec(CatLexica.ASIGNACION);
			GA.Exp0 exp0 = recExp0();
			
			return instR1(iAsigR1(rmem,exp0));
		}
	}
	
	private GA.ParametrosReales recParamsReales() throws IOException{
		if(tokenActual(CatLexica.TRUE) || tokenActual(CatLexica.FALSE) || tokenActual(CatLexica.NUMERO) || tokenActual(CatLexica.PARENTESISAPERTURA)
				|| tokenActual(CatLexica.IDENTIFICADOR) || tokenActual(CatLexica.MENOS) || tokenActual(CatLexica.NOT)){
			GA.ListaParametrosReales lParamReal = recListaParamsReales();
			return paramRealesR1(lParamReal);
		} else{
			return paramRealesR2();
		}
	}
	
	private GA.ListaParametrosReales recListaParamsReales() throws IOException{
		GA.Exp0 exp0a = recExp0();
		GA.ListaParametrosReales lpr = recRListaParamsReales(lParamRealesR2(exp0a));
		return lpr;
	}
	
	private GA.ListaParametrosReales recRListaParamsReales(GA.ListaParametrosReales lpr) throws IOException{
		if(tokenActual(CatLexica.COMA)){
			rec(CatLexica.COMA);
			GA.Exp0 exp0a = recExp0();
			GA.ListaParametrosReales lpr2 = recRListaParamsReales(lParamRealesR1(exp0a,lpr));
			return lpr2;
		}
		else
		{
			return lpr;
		}
	}
	

//	private GA.IAsig recIAsig() throws IOException{
//		
//		GA.Mem mem = recMem();
//		rec(CatLexica.ASIG);
//		GA.Exp0 exp0 = recExp0();
//		
//		return iAsigR1(exp0,mem);
//	}
	
	private GA.INew recINew() throws IOException{
		
		rec(CatLexica.NEW);
		GA.Mem mem = recMem();
		
		return iNewR1(mem);
	}
	
	private GA.IDispose recIDispose() throws IOException{
		
		rec(CatLexica.DELETE);
		GA.Mem mem = recMem();
		
		return iDisposeR1(mem);
	}
	
	private GA.ILectura recILectura() throws IOException{
		
		rec(CatLexica.LECTURA);
		GA.Mem mem = recMem();
		
		return iLecturaR1(mem);
	}
	
	private GA.IEscritura recIEscritura() throws IOException{
		
		rec(CatLexica.ESCRITURA);
		GA.Exp0 exp0 = recExp0();
		
		return iEscrituraR1(exp0);
	}
	
	private GA.IIF recIIF() throws IOException{
		
		rec(CatLexica.IF);
		GA.Casos casos = recCasos();
		rec(CatLexica.FI);
		
		return iifR1(casos);
		
	}
	
	private GA.IDO recIDO() throws IOException{
		
		rec(CatLexica.DO);
		GA.Casos casos = recCasos();
		rec(CatLexica.OD);
		
		return idoR1(casos);
	}
	
	private GA.Casos recCasos() throws IOException{
		
		GA.Caso caso = recCaso();
		GA.Casos rCasos = recRCasos(casosR2(caso));
		
		return rCasos;
	}
	
	private GA.Casos recRCasos(GA.Casos caso_a) throws IOException{
	/*	
		if(tokenActual(CatLexica.CORCHETES)){
			
			rec(CatLexica.CORCHETES);
			GA.Caso caso = recCaso();
			GA.Casos rCasos = recRCasos(casosR1(caso_a, caso));
			
			return rCasos;
		}
		else{
			return caso_a;
		}
		*/
		GA.Caso caso;
		GA.Casos rCasos;
		while(tokenActual(CatLexica.CORCHETES))
		{
			rec(CatLexica.CORCHETES);
			caso = recCaso();
			rCasos = casosR1(caso_a, caso);
			caso_a = rCasos;
		}
		
		return caso_a;
	}
	
	private GA.Caso recCaso() throws IOException{
		
		rec(CatLexica.CASE);
		GA.Exp0 exp0 = recExp0();
		rec(CatLexica.FLECHA);
		GA.Insts instrucciones = recInsts();
		
		return casoR1(exp0, instrucciones);
	}
	
	private GA.Exp0 recExp0() throws IOException{
		
		GA.Exp1 exp1 = recExp1();
		GA.Exp0 rExp0 = recRExp0(exp1);
		
		return rExp0;
	}
	
	private GA.Exp0 recRExp0(GA.Exp1 exp1_a) throws IOException{
		
		if(tokenActual(CatLexica.EQ) || tokenActual(CatLexica.NEQ) || tokenActual(CatLexica.GT) ||
				tokenActual(CatLexica.GE) || tokenActual(CatLexica.LT) || tokenActual(CatLexica.LE))
		{
			GA.OpComp opComparacion = recOpComp();
			GA.Exp1 exp1 = recExp1();
			
			return exp0R1(exp1_a, opComparacion, exp1);
		}
		else
		{
			return exp0R2(exp1_a);
		}
	}
	
	private GA.Exp1 recExp1() throws IOException{
		
		GA.Exp2 exp2_a = recExp2();
		GA.Exp1 rExp1 = recRExp1(exp1R2(exp2_a));
		
		return rExp1;
	}
	
	private GA.Exp1 recRExp1(GA.Exp1 exp2_a) throws IOException{
		/*
		if (tokenActual(CatLexica.SUMA) || tokenActual(CatLexica.RESTA) || tokenActual(CatLexica.OR))
		{
			GA.OpAdi opAditivo = recOpAdi();
			GA.Exp2 exp2 = recExp2();
			GA.Exp1 rExp1 = recRExp1(exp1R1(exp2_a,opAditivo, exp2));
			
			return rExp1;
			
		}
		else 
			return exp2_a;
			*/
		GA.OpAdi opAditivo;
		GA.Exp2 exp2;
		GA.Exp1 rExp1;
		
		while(tokenActual(CatLexica.MAS) || tokenActual(CatLexica.MENOS) || tokenActual(CatLexica.OR))
		{
			opAditivo = recOpAdi();
			exp2 = recExp2();
			rExp1 = exp1R1(exp2_a, opAditivo, exp2);
			exp2_a = rExp1;
		}
		
		return exp2_a;
	}
	
	private GA.Exp2 recExp2() throws IOException{
		
		GA.Exp3 exp3_a = recExp3();
		GA.Exp2 rExp2 = recRExp2(exp2R2(exp3_a));
		
		return rExp2;
	}
	
	private GA.Exp2 recRExp2(GA.Exp2 exp3_a) throws IOException{
		/*
		if (tokenActual(CatLexica.MULT) || tokenActual(CatLexica.DIV) || tokenActual(CatLexica.AND))
		{
			GA.OpMult opMul = recOpMult();
			GA.Exp3 exp3 = recExp3();
			GA.Exp2 rExp2 = recRExp2(exp2R1(exp3_a,opMul,exp3));
			
			return rExp2;
		}
		else 
			return exp3_a;
			*/
		GA.OpMult opMul;
		GA.Exp3 exp3;
		GA.Exp2 rExp2;
		
		while (tokenActual(CatLexica.POR) || tokenActual(CatLexica.ENTRE) || tokenActual(CatLexica.AND))
		{
			opMul = recOpMult();
			exp3 = recExp3();
			rExp2 = recRExp2(exp2R1(exp3_a,opMul,exp3));
			exp3_a = rExp2;
		}
		
		return exp3_a;
	}
	
	private GA.OpComp recOpComp () throws IOException{
		
		if (tokenActual(CatLexica.EQ)){
			rec(CatLexica.EQ);
			
			return compR1();
		}
		else if (tokenActual(CatLexica.NEQ)){
			rec(CatLexica.NEQ);
			
			return compR2();
		}
		else if (tokenActual(CatLexica.GT)){
			rec(CatLexica.GT);
			
			return compR3();
		}
		else if (tokenActual(CatLexica.GE)){
			rec(CatLexica.GE);
			
			return compR4();
		}
		else if (tokenActual(CatLexica.LT)){
			rec(CatLexica.LT);
			
			return compR5();
		}
		else if (tokenActual(CatLexica.LE)){
			rec(CatLexica.LE);
			
			return compR6();
		}
		else {
	          errorSintactico(CatLexica.EQ, CatLexica.NEQ, CatLexica.GT, CatLexica.GE, CatLexica.LT, CatLexica.LE);
	          return null;
	    } 
	}
	private GA.Exp3 recExp3() throws IOException{
		if(tokenActual(CatLexica.MENOS) || tokenActual(CatLexica.NOT))
		{
			GA.OpUna opuna_a = recOpUna();	
			GA.Exp3 exp3_a = recExp3();
			return expr3R1(opuna_a,exp3_a);
		}		
		else
		{
			GA.Exp4 exp4_a = recExp4();
			return expr3R2(exp4_a);
		}
	}
	
	private GA.Exp4 recExp4() throws IOException{
		if(tokenActual(CatLexica.TRUE))
		{
			rec(CatLexica.TRUE);
			return expr4R1();
		}	
		else if(tokenActual(CatLexica.FALSE))
		{
			rec(CatLexica.FALSE);
			return expr4R2();
		}	
		else if(tokenActual(CatLexica.NUMERO))
		{
			Token tNum=token;
			rec(CatLexica.NUMERO);
			return expr4R3(tNum);
		}	
		else if(tokenActual(CatLexica.IDENTIFICADOR))
		{
			GA.Mem mem = recMem();
			return expr4R4(mem);
		}	
		else if(tokenActual(CatLexica.PARENTESISAPERTURA))
		{
			rec(CatLexica.PARENTESISAPERTURA);
	        GA.Exp0 exp0_a = recExp0();
	        rec(CatLexica.PARENTESISCIERRE);
			return expr4R5(exp0_a);
		}
		else {
	          errorSintactico(CatLexica.TRUE, CatLexica.FALSE, CatLexica.NUMERO, CatLexica.IDENTIFICADOR, CatLexica.PARENTESISAPERTURA);
	          return null;
	    } 
	}
	
	private GA.Mem recMem() throws IOException{
		
		if(tokenActual(CatLexica.IDENTIFICADOR)){
			Token tIden=token;
			rec(CatLexica.IDENTIFICADOR);
			GA.Mem mem = recRMem(memR1(tIden));
			return mem;
		}
		else {
	          errorSintactico(CatLexica.IDENTIFICADOR);
	          return null;
	    } 
	}
	
	private GA.Mem recRMem(GA.Mem mem1) throws IOException{
		/*
		if(tokenActual(CatLexica.ACORCHETE)){
			rec(CatLexica.ACORCHETE);
			GA.Exp0 exp0a = recExp0();
			rec(CatLexica.CCORCHETE);
			GA.Mem mem2 = recRMem(memR2(exp0a,mem1)); 
					
			return mem2;
		}
		else if(tokenActual(CatLexica.PUNTO)){
			rec(CatLexica.PUNTO);
			Token tIden=tact;
			rec(CatLexica.IDENT);
			GA.Mem mem2 = recRMem(memR3(tIden,mem1));
			
			return mem2;
		}
		else if(tokenActual(CatLexica.ACCPUNTERO)){
			rec(CatLexica.ACCPUNTERO);
			GA.Mem mem2 = recRMem(memR4(mem1));
			return mem2;
		}
		else
		{
			return mem1;
		}
*/
		GA.Exp0 exp0a;
		GA.Mem mem2;
		Token tIden;
		while(tokenActual(CatLexica.CORCHETEAPERTURA) || tokenActual(CatLexica.PUNTO) || tokenActual(CatLexica.PUNTERO))
		{
			if(tokenActual(CatLexica.CORCHETEAPERTURA)){
				rec(CatLexica.CORCHETEAPERTURA);
				exp0a = recExp0();
				rec(CatLexica.CORCHETECIERRE);
				mem2 = memR2(exp0a, mem1);
				mem1 = mem2;
			}
			else if(tokenActual(CatLexica.PUNTO)){
				rec(CatLexica.PUNTO);
				tIden = token;
				rec(CatLexica.IDENTIFICADOR);
				mem2 = memR3(tIden,mem1);
				mem1 = mem2;
			}
			else if(tokenActual(CatLexica.PUNTERO)){
				rec(CatLexica.PUNTERO);
				mem2 = memR4(mem1);
				mem1 = mem2;
			}
		}
		
		return mem1;
		
	}
	
	private GA.OpAdi recOpAdi() throws IOException{
		
		if(tokenActual(CatLexica.MAS)){
			rec(CatLexica.MAS);
			
			return adiR1();
		}
		else if(tokenActual(CatLexica.MENOS)){
			rec(CatLexica.MENOS);
			
			return adiR2(); 
		}
		else if(tokenActual(CatLexica.OR)){
			rec(CatLexica.OR);
			
			return adiR3();
		}
		else {
	          errorSintactico(CatLexica.MAS, CatLexica.MENOS, CatLexica.OR);
	          return null;
	    } 
	}
	
	private GA.OpMult recOpMult() throws IOException{
		
		if(tokenActual(CatLexica.POR)){
			rec(CatLexica.POR);
			
			return multR1();
		}
		else if(tokenActual(CatLexica.ENTRE)){
			rec(CatLexica.ENTRE);
			
			return multR2(); 
		}
		else if(tokenActual(CatLexica.AND)){
			rec(CatLexica.AND);
			
			return multR3();
		}
		else {
	          errorSintactico(CatLexica.POR, CatLexica.ENTRE, CatLexica.AND);
	          return null;
	    }
	}
	
	private GA.OpUna recOpUna() throws IOException{
		
		if(tokenActual(CatLexica.MENOS)){
			rec(CatLexica.MENOS);
			
			return unaR1();
		}
		else if(tokenActual(CatLexica.NOT)){
			rec(CatLexica.NOT);
			
			return unaR2(); 
		}	
		else {
	          errorSintactico(CatLexica.MENOS, CatLexica.NOT);
	          return null;
	    }
	}
	
	private void rec(CatLexica cat) throws IOException {
        if (tokenActual(cat)) {
        	token = sigToken();
        } else {
            errorSintactico(cat);
        }
    }

    private void errorSintactico(CatLexica... catsEsperadas) {
        System.err.print("(" + token.getFila() + "," + token.getCol() + ")" 
        +"ERROR SINTACTICO: Encontrado " + token + ". Se esperaba alguno de los siguientes elementos: ");
        for (CatLexica catEsperada : catsEsperadas) {
            System.err.print(catEsperada + " ");
        }
        System.err.println();
        System.exit(1);
    }
    
    private boolean tokenActual(CatLexica ... cats) {
        boolean encontrada = false;
        int i=0;
        while (i < cats.length && ! encontrada) {
           encontrada = token.getCategoria() == cats[i];
           i++;
        }
        return encontrada;
    }
    
    private Token sigToken() throws IOException {
       return anaLexico.sigToken();
    }
    
    private GA.Programa progR1(GA.Bloque bloque){
    	if (DEBUG) return tGraAtrib.new ProgR1Debug(bloque);
    	else return tGraAtrib.new ProgR1(bloque);
    }
    
    private GA.Bloque bloqueR1(GA.Decs declaraciones, GA.Insts instrucciones){
    	if (DEBUG) return tGraAtrib.new bloqueR1Debug(declaraciones, instrucciones);
    	else return tGraAtrib.new bloqueR1(declaraciones, instrucciones);
    }
    
    private GA.Bloque bloqueR2(GA.Insts instrucciones){
    	if (DEBUG) return tGraAtrib.new bloqueR2Debug(instrucciones);
    	else return tGraAtrib.new bloqueR2(instrucciones);
    }
    
    private GA.Decs decsR1(GA.Decs dec_a, GA.Dec dec){
    	if (DEBUG) return tGraAtrib.new decsR1Debug(dec_a,dec);
    	else return tGraAtrib.new decsR1(dec_a,dec);
    }
    
    private GA.Decs decsR2(GA.Dec dec_a){
    	if (DEBUG) return tGraAtrib.new decsR2Debug(dec_a);
    	else return tGraAtrib.new decsR2(dec_a);
    }
    
    private GA.Dec decR1(GA.Tipo tipo, Token to){
    	if (DEBUG) return tGraAtrib.new decR1Debug(tipo,to);
    	else return tGraAtrib.new decR1(tipo,to);
    }
    
    private GA.Dec decR2(GA.Tipo tipo, Token to){
    	if (DEBUG) return tGraAtrib.new decR2Debug(tipo,to);
    	else return tGraAtrib.new decR2(tipo,to);
    }
    
    private GA.Dec decR3(Token to, GA.ParametrosFormales paramForm, GA.Bloque bloque){
    	if (DEBUG) return tGraAtrib.new decR3Debug(to,paramForm,bloque);
    	else return tGraAtrib.new decR3(to,paramForm,bloque);
    }
    
    private GA.ParametrosFormales paramFormR1(GA.ListaParametrosFormales lParamForm){
    	if (DEBUG) return tGraAtrib.new parFormR1Debug(lParamForm);
    	else return tGraAtrib.new parFormR1(lParamForm);
    }
    
    private GA.ParametrosFormales paramFormR2(){
    	if (DEBUG) return tGraAtrib.new parFormR2Debug();
    	else return tGraAtrib.new parFormR2();
    }
   
    private GA.ListaParametrosFormales listaParamsFormalesR1(GA.ListaParametrosFormales lParamForm, GA.ParametroFormal paramForm){
    	if (DEBUG) return tGraAtrib.new listaParametrosFormalesR1Debug(lParamForm,paramForm);
    	else return tGraAtrib.new listaParametrosFormalesR1(lParamForm,paramForm);
    }
    
    private GA.ListaParametrosFormales listaParamsFormalesR2(GA.ParametroFormal paramForm){
    	if (DEBUG) return tGraAtrib.new listaParametrosFormalesR2Debug(paramForm);
    	else return tGraAtrib.new listaParametrosFormalesR2(paramForm);
    }
    
    private GA.ParametroFormal paramFormalR1(GA.Tipo tipo, Token t){
    	if (DEBUG) return tGraAtrib.new ParametroFormalR1Debug(tipo, t);
    	else return tGraAtrib.new ParametroFormalR1(tipo, t);
    }
    
    private GA.ParametroFormal paramFormalR2(GA.Tipo tipo, Token t){
    	if (DEBUG) return tGraAtrib.new ParametroFormalR2Debug(tipo, t);
    	else return tGraAtrib.new ParametroFormalR2(tipo, t);
    }
    
    private GA.Tipo tipoR1(){
    	if (DEBUG) return tGraAtrib.new tipoR1Debug();
    	else return tGraAtrib.new tipoR1();
    }
    
    private GA.Tipo tipoR2(){
    	if (DEBUG) return tGraAtrib.new tipoR2Debug();
    	else return tGraAtrib.new tipoR2();
    }
    
    private GA.Tipo tipoR3(Token t, GA.Tipo tipo){
    	if (DEBUG) return tGraAtrib.new tipoR3Debug(t,tipo);
    	else return tGraAtrib.new tipoR3(t,tipo);
    }
    
    private GA.Tipo tipoR4(GA.Campos c){
    	if (DEBUG) return tGraAtrib.new tipoR4Debug(c);
    	else return tGraAtrib.new tipoR4(c);
    }
    
    private GA.Tipo tipoR5(GA.Tipo tipo){
    	if (DEBUG) return tGraAtrib.new tipoR5Debug(tipo);
    	else return tGraAtrib.new tipoR5(tipo);
    }
    
    private GA.Tipo tipoR6(Token t){
    	if (DEBUG) return tGraAtrib.new tipoR6Debug(t);
    	else return tGraAtrib.new tipoR6(t);
    }
    
    private GA.Campos camposR1(GA.Campos campos, GA.Campo campo){
    	if (DEBUG) return tGraAtrib.new camposR1Debug(campos,campo);
    	else return tGraAtrib.new camposR1(campos,campo);
    }
    
    private GA.Campos camposR2(GA.Campo campo){
    	if (DEBUG) return tGraAtrib.new camposR2Debug(campo);
    	else return tGraAtrib.new camposR2(campo);
    }
    
    private GA.Campo campoR1(GA.Tipo tipo, Token t){
    	if (DEBUG) return tGraAtrib.new campoR1Debug(tipo,t);
    	else return tGraAtrib.new campoR1(tipo,t);
    }
    
    private GA.Insts instsR1(GA.Insts insts, GA.Inst inst){
    	if (DEBUG) return tGraAtrib.new instsR1Debug(insts,inst);
    	else return tGraAtrib.new instsR1(insts,inst);
    }
    
    private GA.Insts instsR2(GA.Inst inst){
    	if (DEBUG) return tGraAtrib.new instsR2Debug(inst);
    	else return tGraAtrib.new instsR2(inst);
    }
    
    private GA.Inst instR1(GA.IAsig asig){
    	if (DEBUG) return tGraAtrib.new instR1Debug(asig);
    	else return tGraAtrib.new instR1(asig);
    }
    
    private GA.Inst instR2(GA.IIF iif){
    	if (DEBUG) return tGraAtrib.new instR2Debug(iif);
    	else return tGraAtrib.new instR2(iif);
    }
    
    private GA.Inst instR3(GA.IDO ido){
    	if (DEBUG) return tGraAtrib.new instR3Debug(ido);
    	else return tGraAtrib.new instR3(ido);
    }
    
    private GA.Inst instR4(GA.INew inew){
    	if (DEBUG) return tGraAtrib.new instR4Debug(inew);
    	else return tGraAtrib.new instR4(inew);
    }
    
    private GA.Inst instR5(GA.IDispose idi){
    	if (DEBUG) return tGraAtrib.new instR5Debug(idi);
    	else return tGraAtrib.new instR5(idi);
    }
    
    private GA.Inst instR6(GA.ILectura il){
    	if (DEBUG) return tGraAtrib.new instR6Debug(il);
    	else return tGraAtrib.new instR6(il);
    }
    
    private GA.Inst instR7(GA.IEscritura ie){
    	if (DEBUG) return tGraAtrib.new instR7Debug(ie);
    	else return tGraAtrib.new instR7(ie);
    }
    
    private GA.Inst instR8(GA.ILlamada ill){
    	if (DEBUG) return tGraAtrib.new instR8Debug(ill);
    	else return tGraAtrib.new instR8(ill);
    }
    
    private GA.ParametrosReales paramRealesR1(GA.ListaParametrosReales lParamReales){
    	if (DEBUG) return tGraAtrib.new ParametroRealesR1Debug(lParamReales);
    	else return tGraAtrib.new ParametroRealesR1(lParamReales);
    }
    
    private GA.ParametrosReales paramRealesR2(){
    	if (DEBUG) return tGraAtrib.new ParametroRealesR2Debug();
    	else return tGraAtrib.new ParametroRealesR2();
    }
    
    private GA.ListaParametrosReales lParamRealesR1(GA.Exp0 exp0a,GA.ListaParametrosReales lpr){
    	if (DEBUG) return tGraAtrib.new ListaParRealR1Debug(lpr,exp0a);
    	else return tGraAtrib.new ListaParRealR1(lpr,exp0a);
    }    
    
    private GA.ListaParametrosReales lParamRealesR2(GA.Exp0 exp0a){
    	if (DEBUG) return tGraAtrib.new ListaParRealR2Debug(exp0a);
    	else return tGraAtrib.new ListaParRealR2(exp0a);
    }    
    
    private GA.IAsig iAsigR1(GA.Mem mem, GA.Exp0 exp0){
    	if (DEBUG) return tGraAtrib.new iAsigR1Debug(mem,exp0);
    	else return tGraAtrib.new iAsigR1(mem,exp0);
    }
    
    private GA.ILlamada iLlamadaR1(Token iden, GA.ParametrosReales paramReal){
    	if (DEBUG) return tGraAtrib.new illamaR1Debug(iden,paramReal);
    	else return tGraAtrib.new illamaR1(iden,paramReal);
    }
    
    private GA.INew iNewR1(GA.Mem mem){
    	if (DEBUG) return tGraAtrib.new INewR1Debug(mem);
    	else return tGraAtrib.new INewR1(mem);
    }
    private GA.IDispose iDisposeR1(GA.Mem mem){
    	if (DEBUG) return tGraAtrib.new IDispR1Debug(mem);
    	else return tGraAtrib.new IDispR1(mem);
    }
    
    private GA.ILectura iLecturaR1(GA.Mem mem){
    	if (DEBUG) return tGraAtrib.new iLectR1Debug(mem);
    	else return tGraAtrib.new iLectR1(mem);
    }
   
 	private GA.IEscritura iEscrituraR1(GA.Exp0 exp0){
    	if (DEBUG) return tGraAtrib.new iEscrR1Debug(exp0);
    	else return tGraAtrib.new iEscrR1(exp0);
    }
    
    
    private GA.IIF iifR1(GA.Casos casos){
    	if (DEBUG) return tGraAtrib.new iifR1Debug(casos);
    	else return tGraAtrib.new iifR1(casos);
    }
    
    private GA.IDO idoR1(GA.Casos casos){
    	if (DEBUG) return tGraAtrib.new idoR1Debug(casos);
    	else return tGraAtrib.new idoR1(casos);
    }
    
    private GA.Casos casosR1(GA.Casos casos, GA.Caso caso){
    	if (DEBUG) return tGraAtrib.new CasosR1Debug(casos,caso);
    	else return tGraAtrib.new CasosR1(casos,caso);
    }
    
    private GA.Casos casosR2(GA.Caso caso){
    	if (DEBUG) return tGraAtrib.new CasosR2Debug(caso);
    	else return tGraAtrib.new CasosR2(caso);
    }
    
    private GA.Caso casoR1(GA.Exp0 exp0, GA.Insts insts){
    	if (DEBUG) return tGraAtrib.new CasoR1Debug(exp0,insts);
    	else return tGraAtrib.new CasoR1(exp0,insts);
    }
    
    private GA.Exp0 exp0R1(GA.Exp1 exp1_a, GA.OpComp opComparacion, GA.Exp1 exp1){
    	if (DEBUG) return tGraAtrib.new Exp0R1Debug(exp1_a,opComparacion,exp1);
    	else return tGraAtrib.new Exp0R1(exp1_a,opComparacion,exp1);
    }
    
    private GA.Exp0 exp0R2(GA.Exp1 exp1){
    	if (DEBUG) return tGraAtrib.new exp0R2Debug(exp1);
    	else return tGraAtrib.new exp0R2(exp1);
    }
    
    private GA.Exp1 exp1R1(GA.Exp1 exp2_a, GA.OpAdi opAdi, GA.Exp2 exp2){
    	if (DEBUG) return tGraAtrib.new exp1R1Debug(exp2_a,opAdi,exp2);
    	else return tGraAtrib.new exp1R1(exp2_a,opAdi,exp2);
    }
    
    private GA.Exp1 exp1R2(GA.Exp2 exp2){
    	if (DEBUG) return tGraAtrib.new exp1R2Debug(exp2);
    	else return tGraAtrib.new exp1R2(exp2);
    }  
    
    private GA.Exp2 exp2R1(GA.Exp2 exp3_a, GA.OpMult opMult, GA.Exp3 exp3){
    	if (DEBUG) return tGraAtrib.new exp2R1Debug(exp3_a,opMult,exp3);
    	else return tGraAtrib.new exp2R1(exp3_a,opMult,exp3);
    }
    
    private GA.Exp2 exp2R2(GA.Exp3 exp3){
    	if (DEBUG) return tGraAtrib.new exp2R2Debug(exp3);
    	else return tGraAtrib.new exp2R2(exp3);
    }
    
    private GA.OpComp compR1(){
    	if (DEBUG) return tGraAtrib.new compR1();
    	else return tGraAtrib.new compR1();
    }
    
    private GA.OpComp compR2(){
    	if (DEBUG) return tGraAtrib.new compR2();
    	else return tGraAtrib.new compR2();
    }
    
    private GA.OpComp compR3(){
    	if (DEBUG) return tGraAtrib.new compR3();
    	else return tGraAtrib.new compR3();
    }
    
    private GA.OpComp compR4(){
    	if (DEBUG) return tGraAtrib.new compR4();
    	else return tGraAtrib.new compR4();
    }
    
    private GA.OpComp compR5(){
    	if (DEBUG) return tGraAtrib.new compR5();
    	else return tGraAtrib.new compR5();
    }
    
    private GA.OpComp compR6(){
    	if (DEBUG) return tGraAtrib.new compR6();
    	else return tGraAtrib.new compR6();
    }
    
    private GA.Exp3 expr3R1(GA.OpUna opuna_a, GA.Exp3 exp3_a){
    	if (DEBUG) return tGraAtrib.new exp3R1(opuna_a,exp3_a);
    	else return tGraAtrib.new exp3R1(opuna_a,exp3_a);
    }
    
    private GA.Exp3 expr3R2(GA.Exp4 exp4_a){
    	if (DEBUG) return tGraAtrib.new exp3R2(exp4_a);
    	else return tGraAtrib.new exp3R2(exp4_a);
    }
    
    private GA.Exp4 expr4R1(){
    	if (DEBUG) return tGraAtrib.new exp4R1();
    	else return tGraAtrib.new exp4R1();
    }
    
    private GA.Exp4 expr4R2(){
    	if (DEBUG) return tGraAtrib.new exp4R2();
    	else return tGraAtrib.new exp4R2();
    }
    
    private GA.Exp4 expr4R3(Token to){
    	if (DEBUG) return tGraAtrib.new exp4R3(to);
    	else return tGraAtrib.new exp4R3(to);
    }
    
    private GA.Exp4 expr4R4(GA.Mem mem){
    	if (DEBUG) return tGraAtrib.new exp4R4(mem);
    	else return tGraAtrib.new exp4R4(mem);
    }
    
    private GA.Exp4 expr4R5(GA.Exp0 exp0){
    	if (DEBUG) return tGraAtrib.new exp4R5(exp0);
    	else return tGraAtrib.new exp4R5(exp0);
    }
    
    private GA.Mem memR1(Token to){
    	if (DEBUG) return tGraAtrib.new memR1(to);
    	else return tGraAtrib.new memR1(to);
    }
    
    private GA.Mem memR2(GA.Exp0 exp0a,GA.Mem mem){
    	if (DEBUG) return tGraAtrib.new memR2(exp0a,mem);
    	else return tGraAtrib.new memR2( exp0a,mem);
    }
    private GA.Mem memR3(Token to,GA.Mem mem){
    	if (DEBUG) return tGraAtrib.new memR3(to,mem);
    	else return tGraAtrib.new memR3(to,mem);
    }
    private GA.Mem memR4(GA.Mem mem){
    	if (DEBUG) return tGraAtrib.new memR4(mem);
    	else return tGraAtrib.new memR4(mem);
    }
    
    private GA.OpAdi adiR1(){
    	if (DEBUG) return tGraAtrib.new adiR1();
    	else return tGraAtrib.new adiR1();
    }
    
    private GA.OpAdi adiR2(){
    	if (DEBUG) return tGraAtrib.new adiR2();
    	else return tGraAtrib.new adiR2();
    }
    
    private GA.OpAdi adiR3(){
    	if (DEBUG) return tGraAtrib.new adiR3();
    	else return tGraAtrib.new adiR3();
    }
    
    private GA.OpMult multR1(){
    	if (DEBUG) return tGraAtrib.new multR1();
    	else return tGraAtrib.new multR1();
    } 
    
    private GA.OpMult multR2(){
    	if (DEBUG) return tGraAtrib.new multR2();
    	else return tGraAtrib.new multR2();
    } 
    
    private GA.OpMult multR3(){
    	if (DEBUG) return tGraAtrib.new multR3();
    	else return tGraAtrib.new multR3();
    } 
    
    private GA.OpUna unaR1(){
    	if (DEBUG) return tGraAtrib.new unaR1();
    	else return tGraAtrib.new unaR1();
    } 
    
    private GA.OpUna unaR2(){
    	if (DEBUG) return tGraAtrib.new unaR2();
    	else return tGraAtrib.new unaR2();
    } 
}